package com.applcn.user.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.applcn.core.base.PageVO;
import com.applcn.core.base.Result;
import com.applcn.core.redis.RedisDisLock;
import com.applcn.shop.client.ShopClient;
import com.applcn.user.config.WxappConfig;
import com.applcn.user.constant.AuthRangeEnum;
import com.applcn.user.constant.GenderEnum;
import com.applcn.user.constant.RedisKeyConstants;
import com.applcn.user.dto.*;
import com.applcn.user.dto.merchant.*;
import com.applcn.user.repo.entity.MerchantEntity;
import com.applcn.user.repo.entity.RoleEntity;
import com.applcn.user.repo.entity.UserEntity;
import com.applcn.user.repo.entity.UserRoleEntity;
import com.applcn.user.repo.repository.MerchantRepository;
import com.applcn.user.repo.repository.RoleRepository;
import com.applcn.user.repo.repository.UserRepository;
import com.applcn.user.repo.repository.UserRoleRepository;
import com.applcn.user.threadlocal.UserRequestContextHolder;
import com.applcn.user.utils.JwtUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * com.applcn.user.service
 * Description:
 *
 * @author: 大妖怪
 * @date: 2020-01-18 00:46
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {

    private final UserRepository userRepository;
    private final RestTemplate restTemplate;
    private final WxappConfig wxappConfig;
    private final RedisDisLock redisDisLock;
    private final JwtUtil jwtUtil;
    private final MenuService menuService;
    private final RoleService roleService;
    private final ShopClient shopClient;
    private final MerchantRepository merchantRepository;
    private final RoleRepository roleRepository;
    private final UserRoleRepository userRoleRepository;

    /**
     * 微信登录
     * <p>1.获取微信配置</p>
     * <p>2.将前端传来临时code和微信配置一起去请求微信服务器获取用户信息</p>
     * <p>3.用openId查询用户</p>
     * <p>4.用户存在，视为等登录</p>
     * <p>5.用户不存在，视为注册</p>
     * <p>6.jwt生成token并返回</p>
     *
     * @param wxLoginDTO
     * @return
     */
    public Result wxLogin(WxLoginDTO wxLoginDTO) {
        // 获取openid
        ResponseEntity<WxOpenDTO> entity = restTemplate.getForEntity(wxappConfig.getJscode2SessionUrl(wxLoginDTO.getCode()), WxOpenDTO.class);
        Assert.isTrue(entity.getStatusCode().is2xxSuccessful(), "登录失败！");
        Assert.isTrue(!(entity.getBody() == null || StrUtil.isBlank(entity.getBody().getOpenid())), "登录失败！");
        WxOpenDTO wxOpenDTO = entity.getBody();
        String key = RedisKeyConstants.WX_APP_LOGIN_LOCK_KEY + wxOpenDTO.getOpenid();
        try {
            boolean isLock = redisDisLock.lock(key);
            Assert.isTrue(isLock, "点击太快了，请稍后操作");
            UserEntity userEntity = userRepository.wxLogin(wxOpenDTO.getOpenid(), wxLoginDTO.getNickName(), wxLoginDTO.getGender(), wxLoginDTO.getAvatarUrl());

            CreateTokenDTO createTokenDTO = new CreateTokenDTO();
            createTokenDTO.setUserId(userEntity.getId());
            String token = jwtUtil.createToken(createTokenDTO);
            return Result.ok().putData(token);
        } finally {
            redisDisLock.unlock(key);
        }
    }

    /**
     * 解析jwt token
     *
     * @param token
     * @return
     */
    public CreateTokenDTO resolveToken(String token) {
        CreateTokenDTO dto = jwtUtil.resolveToken(token);
        return dto;
    }

    /**
     * 创建用户
     *
     * @param dto
     * @return
     */
    public Result createUserByPhoneAndPassword(CreateUserDTO dto) {
        String key = RedisKeyConstants.CREATE_USER_BY_PHONE_AND_PASSWORD_LOCK_KEY + dto.getPhone();
        try {
            boolean isLock = redisDisLock.lock(key);
            Assert.isTrue(isLock, "点击太快，请稍后再试");
            UserEntity userEntity = userRepository.getByPhone(dto.getPhone());
            Assert.isNull(userEntity, "手机号已注册！");
            userEntity = new UserEntity();
            userEntity.setPhone(dto.getPhone());
            userEntity.setPassword(SecureUtil.md5(dto.getPassword()));
            userEntity.setGender(GenderEnum.UNKNOWN);
            userEntity.setNickName(dto.getPhone());
            userRepository.createUser(userEntity);
            return Result.ok().putData(userEntity.getId());
        } finally {
            redisDisLock.unlock(key);
        }
    }

    /**
     * 获取当前登录用户信息
     *
     * @return
     */
    public Result getCurrentUserInfo() {
        Long userId = UserRequestContextHolder.getUserId();
        Assert.notNull(userId, "用户未登录！");
        UserEntity userEntity = userRepository.getById(userId);
        GetCurrentUserInfoRespDTO dto = new GetCurrentUserInfoRespDTO();
        dto.setName(userEntity.getNickName());
        dto.setAvatar(userEntity.getAvatarUrl());
        dto.setRoles(Lists.newArrayList("admin"));
        return Result.ok().putData(dto);
    }

    /**
     * 初始化店铺菜单信息
     *
     * @param initShopMenuDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result initShopMenu(InitShopMenuDTO initShopMenuDTO) {
        // 创建超级管理员角色
        RoleEntity roleEntity = roleService.createUserRole("超级管理员", initShopMenuDTO.getShopId(), initShopMenuDTO.getUserId(), AuthRangeEnum.MERCHANT);
        // 创建菜单列表，拷贝一份shopId为空0的菜单
        menuService.copyMenu(initShopMenuDTO.getShopId(), roleEntity.getId());

        return Result.ok();
    }

    /**
     * 分页查询管理列表
     *
     * @param dto
     * @return
     */
    public Result pageAdmin(PageAdminReqDTO dto) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请选择店铺！");
        Result shopResult = shopClient.listMerchantByShopId(shopId);
        Assert.isTrue(shopResult.isSuccess(), "查询店铺绑定商户列表失败");
        List<Long> merchantIds = (List<Long>) shopResult.getData();

        List<UserEntity> userEntityList = new ArrayList<>();
        if (StrUtil.isNotBlank(dto.getNickName())) {
            userEntityList = userRepository.searchUserByName(dto.getNickName(), dto.getPhone());
        }
        List<Long> userIds = userEntityList.stream().map(UserEntity::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(merchantIds) && CollectionUtil.isEmpty(userIds)) {
            PageVO pageVO = new PageVO();
            pageVO.setCurrent(dto.getPageNum());
            pageVO.setPages(0);
            pageVO.setSize(dto.getPageSize());
            pageVO.setTotal(0);
            return Result.ok().putData(pageVO);
        }
        IPage<MerchantEntity> merchantEntityIPage = merchantRepository.pageMerchantByIds(dto.getPageNum(), dto.getPageSize(), merchantIds, userIds);
        List<PageAdminRespDTO> pageAdminRespDTOList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(merchantEntityIPage.getRecords())) {
            userEntityList = userRepository.listByIds(merchantEntityIPage.getRecords().stream().map(MerchantEntity::getUserId).collect(Collectors.toList()));
        }
        for (MerchantEntity merchantEntity : merchantEntityIPage.getRecords()) {
            PageAdminRespDTO pageAdminRespDTO = new PageAdminRespDTO();
            pageAdminRespDTO.setMerchantId(merchantEntity.getId());
            UserEntity userEntity = userEntityList.stream().
                    filter(e -> e.getId().equals(merchantEntity.getUserId())).findFirst().get();
            pageAdminRespDTO.setUserNick(userEntity.getNickName());
            pageAdminRespDTO.setPhone(userEntity.getPhone());
            List<RoleEntity> roleEntityList = roleRepository.getRoleByUserIdAndAuthRange(shopId, userEntity.getId(), AuthRangeEnum.MERCHANT);
            List<String> roleNames = roleEntityList.stream().map(RoleEntity::getName).collect(Collectors.toList());
            pageAdminRespDTO.setRoleNames(roleNames);
            pageAdminRespDTO.setCreateTime(merchantEntity.getCreateTime());
            pageAdminRespDTO.setUpdateTime(merchantEntity.getUpdateTime());
            pageAdminRespDTOList.add(pageAdminRespDTO);
        }
        PageVO<PageAdminRespDTO> pageVO = new PageVO();
        pageVO.setRecords(pageAdminRespDTOList);
        pageVO.assemblePage(merchantEntityIPage);

        return Result.ok().putData(pageVO);
    }

    /**
     * 通过手机号搜索商户
     *
     * @param phone
     * @return
     */
    public Result searchAdminByPhone(String phone) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请选择店铺！");
        UserEntity userEntity = userRepository.getByPhone(phone);
        Assert.notNull(userEntity, "用户不存在！");
        MerchantEntity merchantEntity = merchantRepository.getByUserId(userEntity.getId());
        Result result = shopClient.listMerchantByShopId(shopId);
        Assert.isTrue(result.isSuccess(), "查询商户列表失败");
        List<Long> merchantIds = JSONObject.parseObject(result.getData().toString(), new TypeReference<List<Long>>() {
        });
        if (CollectionUtil.isNotEmpty(merchantIds)) {
            boolean contains = merchantIds.stream().anyMatch(merchantId -> merchantId.equals(merchantEntity.getId()));
            Assert.isTrue(!contains, "用户已是管理员，不需要重复添加");
        }
        SearchAdminByPhoneRespDTO dto = new SearchAdminByPhoneRespDTO();
        dto.setMerchantId(merchantEntity.getId());
        dto.setName(userEntity.getNickName());
        return Result.ok().putData(dto);
    }

    /**
     * 添加管理员
     *
     * @param addAdminDTO
     * @return
     */
    @GlobalTransactional(timeoutMills = 300000, name = "save-admin")
    public Result addAdmin(AddAdminDTO addAdminDTO) {
        Long shopId = UserRequestContextHolder.getShopId();
        Assert.notNull(shopId, "请选择店铺！");
        // 查询商户
        List<MerchantEntity> merchantEntityList = merchantRepository.listByIds(addAdminDTO.getMerchantIds());
        Assert.isTrue(CollectionUtil.isNotEmpty(merchantEntityList)
                && merchantEntityList.size() == addAdminDTO.getMerchantIds().size(), "请输入正确的商户id");

        // 查询角色
        List<RoleEntity> roleEntityList = roleRepository.getShopRoleList(AuthRangeEnum.MERCHANT, shopId, addAdminDTO.getRoleIds());
        Assert.isTrue(CollectionUtil.isNotEmpty(roleEntityList)
                && roleEntityList.size() == addAdminDTO.getRoleIds().size());
        for (MerchantEntity merchantEntity : merchantEntityList) {
            // 储存用户角色
            UserEntity userEntity = userRepository.getById(merchantEntity.getUserId());
            List<UserRoleEntity> userRoleEntityList = new ArrayList<>();
            for (Long roleId : addAdminDTO.getRoleIds()) {
                UserRoleEntity userRoleEntity = new UserRoleEntity();
                userRoleEntity.setUserId(userEntity.getId());
                userRoleEntity.setRoleId(roleId);
                userRoleEntityList.add(userRoleEntity);
            }
            userRoleRepository.saveBatch(userRoleEntityList);
        }

        // 绑定店铺和用户关系
        shopClient.bindMerchants(shopId, addAdminDTO.getMerchantIds());

        return Result.ok();
    }
}
